Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 | import { NextResponse } from 'next/server' import { withAuth } from '@/lib/auth/withAuth' import { canPerformAction } from '@/lib/classroom' import { getSessionPlan } from '@/lib/curriculum' import { updateSessionPlanResults } from '@/lib/curriculum/session-planner' import { getUserId } from '@/lib/viewer' /** * PATCH /api/curriculum/[playerId]/sessions/plans/[planId]/results/[resultIndex] * Edit a specific result in the session plan * * Actions: * - mark_correct: Change an incorrect result to correct * - exclude: Mark result as excluded from tracking (source: 'teacher-excluded') * - include: Remove exclusion (restore original source) */ export const PATCH = withAuth(async (request, { params }) => { const { playerId, planId, resultIndex: resultIndexStr, } = (await params) as { playerId: string; planId: string; resultIndex: string } const resultIndex = parseInt(resultIndexStr, 10) if (isNaN(resultIndex) || resultIndex < 0) { return NextResponse.json({ error: 'Invalid result index' }, { status: 400 }) } try { // Authorization: require 'start-session' permission (parent or teacher-present) const userId = await getUserId() const canModify = await canPerformAction(userId, playerId, 'start-session') if (!canModify) { return NextResponse.json({ error: 'Not authorized' }, { status: 403 }) } const body = await request.json() const { action } = body // Get current plan const plan = await getSessionPlan(planId) if (!plan) { return NextResponse.json({ error: 'Plan not found' }, { status: 404 }) } // Validate result index if (resultIndex >= plan.results.length) { return NextResponse.json({ error: 'Result index out of bounds' }, { status: 400 }) } const result = plan.results[resultIndex] const updatedResults = [...plan.results] switch (action) { case 'mark_correct': { if (result.isCorrect) { return NextResponse.json({ error: 'Result is already correct' }, { status: 400 }) } // Mark as correct and recalculate mastery weight const epochNumber = result.epochNumber ?? 0 const masteryWeight = 1.0 / 2 ** epochNumber updatedResults[resultIndex] = { ...result, isCorrect: true, masteryWeight, // Add marker that this was teacher-corrected for audit trail source: 'teacher-corrected' as const, } // Handle retry queue implications // If this was epoch 0 and there are retry results for this slot, we should handle that // For now, we'll leave the retry results in place - they still happened // The BKT will recalculate based on the corrected result break } case 'exclude': { if (result.source === 'teacher-excluded') { return NextResponse.json({ error: 'Result is already excluded' }, { status: 400 }) } // Store original source so we can restore it const originalSource = result.source updatedResults[resultIndex] = { ...result, source: 'teacher-excluded' as const, // Store original source in a new field for potential restoration _originalSource: originalSource, } as typeof result & { _originalSource?: string } break } case 'include': { if (result.source !== 'teacher-excluded') { return NextResponse.json({ error: 'Result is not excluded' }, { status: 400 }) } // Restore original source const originalSource = (result as typeof result & { _originalSource?: string }) ._originalSource updatedResults[resultIndex] = { ...result, source: (originalSource as typeof result.source) ?? 'practice', } // Remove the _originalSource field delete ( updatedResults[resultIndex] as typeof result & { _originalSource?: string } )._originalSource break } default: return NextResponse.json( { error: 'Invalid action. Must be: mark_correct, exclude, or include', }, { status: 400 } ) } // Update the plan with modified results const updatedPlan = await updateSessionPlanResults(planId, updatedResults) return NextResponse.json({ plan: { ...updatedPlan, createdAt: updatedPlan.createdAt instanceof Date ? updatedPlan.createdAt.getTime() : updatedPlan.createdAt, approvedAt: updatedPlan.approvedAt instanceof Date ? updatedPlan.approvedAt.getTime() : updatedPlan.approvedAt, startedAt: updatedPlan.startedAt instanceof Date ? updatedPlan.startedAt.getTime() : updatedPlan.startedAt, completedAt: updatedPlan.completedAt instanceof Date ? updatedPlan.completedAt.getTime() : updatedPlan.completedAt, }, }) } catch (error) { console.error('Error updating result:', error) return NextResponse.json({ error: 'Failed to update result' }, { status: 500 }) } }) |